package com.funny.stock.trade.service.monitor;

import com.funny.stock.base.redis.RedisTool;
import com.funny.stock.base.util.ResultUtil;
import com.funny.stock.base.vo.Result;
import com.funny.stock.base.vo.TradeType;
import com.funny.stock.data.collect.StockMonitorStrategy;
import com.funny.stock.data.collect.bean.StockMonitorContext;
import com.funny.stock.data.vo.RealtimeStockData;
import com.funny.stock.data.vo.StockConstants;
import com.funny.stock.trade.dao.pojo.DayTradeProfitDO;
import com.funny.stock.trade.dao.pojo.GridTradePlanVarDO;
import com.funny.stock.trade.service.ComposeStockActionService;
import com.funny.stock.trade.service.GridTradePlanService;
import com.funny.stock.trade.service.GridTradePlanVarService;
import com.funny.stock.trade.service.StockMsgNotifyService;
import com.funny.stock.trade.util.PriceUtils;
import com.funny.stock.trade.vo.GridTradePlanVO;
import com.funny.stock.trade.vo.TradeActionInfo;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author pengl
 * @date 2020/11/18
 */
@Component
public class GridTradeMonitorService implements StockMonitorStrategy {
    private static final Logger logger = LoggerFactory.getLogger(GridTradeMonitorService.class);

    @Autowired
    GridTradePlanService gridTradePlanService;

    @Autowired
    GridTradePlanVarService gridTradePlanVarService;

    @Autowired
    RedisTool redisTool;

//    @Autowired
//    MailService mailService;

    @Autowired
    TaskExecutor taskExecutor;

//    @Autowired
//    DayTradeRecordService dayTradeRecordService;

    @Autowired
    ComposeStockActionService actionService;

    @Autowired
    StockMsgNotifyService stockMsgNotifyService;

    @Override
    public void onMonitor(StockMonitorContext context) {
        List<GridTradePlanVO> plans = gridTradePlanService.queryTradingPlanList();

//        logger.info("执行网格交易计划的数目:{}",plans.size());
        if(plans.size() == 0) return;

        //如果单次执行计划时长超过更新数据时长，存在问题，待解决
        StopWatch stopWatch = new StopWatch("gridPlan");
        stopWatch.start();

        Map<String, RealtimeStockData> dataMap = context.getDataMap();
        for (GridTradePlanVO plan : plans) {
            RealtimeStockData realtimeStockData = dataMap.get(plan.getStockCode());
            if(realtimeStockData == null) continue;

            invokePlan(realtimeStockData, plan);
        }

        stopWatch.stop();

        if(stopWatch.getTotalTimeMillis() >= StockConstants.DATA_REFRESH_INTERVAL_SECONDS * 500) {
            logger.warn("执行网格交易交计划消耗时长/s:{}", stopWatch.getTotalTimeSeconds());
        }
        logger.info("执行网格交易计划的数目:{},耗时/ms:{}",plans.size(),stopWatch.getTotalTimeMillis());
    }

    private void invokePlan(RealtimeStockData realtimeStockData,GridTradePlanVO planDO){
//        logger.info("invokePlan:id:{},code:{}", planDO.getId(),planDO.getStockCode());

        String tradeKey = StockConstants.KEY_GRID_TRADE_PREFIX + planDO.getStockCode() + "-" + planDO.getId();

//        String lastTradePrice = (String) getHashValue(tradeKey,  StockConstants.KEY_GRID_LAST_TRADE_PRICE);
        String nextSellPriceStr = (String) getHashValue(tradeKey,  StockConstants.KEY_GRID_NEXT_SELL_PRICE);
        String nextBuyPriceStr = (String) getHashValue(tradeKey,  StockConstants.KEY_GRID_NEXT_BUY_PRICE);

        BigDecimal curTradeIncreasePrice = null;
        BigDecimal curTradeDecreasePrice = null;

        if(StringUtils.isEmpty(nextSellPriceStr) || StringUtils.isEmpty(nextBuyPriceStr)){
            GridTradePlanVarDO planVarDO = gridTradePlanVarService.findByPlanId(planDO.getId());
            if(planVarDO == null) {
                logger.info("planVarDO is null:{}",planDO.getId());
//                GridTradePlanVarDO tempPlanVar = new GridTradePlanVarDO();
//
//                saveTradePlanPriceToRedis(tradeKey, planDO.getBasePrice(), planDO.getIncreasePer(),
//                        planDO.getDecreasePer(),tempPlanVar);
//
//                tempPlanVar.setPlanId(planDO.getId());
//                tempPlanVar.setUpdateTime(new Date());
//
//                gridTradePlanVarService.savePlan(tempPlanVar);
//                logger.warn("执行计划失败，股票无下次买入卖出价格,{}",planDO.getStockCode());
                return;
            }

            curTradeIncreasePrice = planVarDO.getNextSellPrice();
            curTradeDecreasePrice = planVarDO.getNextBuyPrice();

            putHashValue(tradeKey, StockConstants.KEY_GRID_NEXT_BUY_PRICE, curTradeDecreasePrice.toString());
            putHashValue(tradeKey, StockConstants.KEY_GRID_NEXT_SELL_PRICE, curTradeIncreasePrice.toString());

        }else{
            curTradeIncreasePrice = new BigDecimal(nextSellPriceStr);
            curTradeDecreasePrice = new BigDecimal(nextBuyPriceStr);
        }


        //获取下次买入和卖出的价格，监控买入卖出
//        BigDecimal realPercent = BigDecimal.valueOf(realtimeStockData.getPrice())
//                .multiply(new BigDecimal(100))
//                .setScale(2,BigDecimal.ROUND_HALF_UP);
//        logger.info("percent:{},price:{},arrow:{}",realPercent.toString(),realtimeStockData.getPrice(),realtimeStockData.getArrow());

//        TradeActionInfo tradeActionInfo = buildTradeActionInfo(realtimeStockData, planDO, StockConstants.STOCK_STRATEGY_GRID, TradeType.SELL);
//        logger.info("info:{}", JSON.toJSONString(tradeActionInfo));

        BigDecimal realPrice = BigDecimal.valueOf(realtimeStockData.getPrice());
        boolean isTradeUp = realPrice.compareTo(curTradeIncreasePrice) >= 0;
//        logger.info("网格交易计划:{},下次预计买入金额:{},卖出金额:{},当前金额:{}",realtimeStockData.getSymbol(),curTradeDecreasePrice,curTradeIncreasePrice,realPrice);

        if(isTradeUp || realPrice.compareTo(curTradeDecreasePrice) <= 0){
            logger.info("价格触发交易通知:{}",realPrice);

//            BigDecimal increasePer = planDO.getIncreasePer().divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP).add(BigDecimal.ONE);
//            BigDecimal decreasePer = planDO.getDecreasePer().divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP).add(BigDecimal.ONE);
//            BigDecimal nSellPrice = realPrice.multiply(increasePer).setScale(2,BigDecimal.ROUND_HALF_UP);
//            BigDecimal nBuyPrice = realPrice.multiply(decreasePer).setScale(2,BigDecimal.ROUND_HALF_UP);
            //触发交易涨幅或跌幅，发送邮件，设置下一次监控触发条件
            putHashValue(tradeKey, StockConstants.KEY_GRID_LAST_TRADE_PRICE, realPrice.toString());

            GridTradePlanVarDO tempPlanVar = new GridTradePlanVarDO();
            saveTradePlanPriceToRedis(tradeKey, realPrice, planDO.getIncreasePer(), planDO.getDecreasePer(),planDO.getId(),tempPlanVar);


//            putHashValue(tradeKey, StockConstants.KEY_GRID_NEXT_SELL_PRICE, nSellPrice.toString());
//            putHashValue(tradeKey, StockConstants.KEY_GRID_NEXT_BUY_PRICE, nBuyPrice.toString());
//            expireTime(tradeKey, StockConstants.STOCK_EXPIRE_HOURS, TimeUnit.HOURS);

            //执行买入卖出操作记录,发送邮件提醒
            doTradeAction(realtimeStockData, planDO, tempPlanVar, isTradeUp);
        }
    }

    public void doTradeAction(RealtimeStockData realtimeStockData,GridTradePlanVO planDO,GridTradePlanVarDO planVarDO,boolean isUp) {
//        GridTradePlanVarDO planVarDO = new GridTradePlanVarDO();
        TradeActionInfo actionInfo = buildTradeActionInfo(realtimeStockData, planDO, StockConstants.STOCK_STRATEGY_GRID, isUp ? TradeType.SELL : TradeType.BUY);

        taskExecutor.execute(()-> {
            if(planVarDO != null)
                gridTradePlanVarService.savePlan(planVarDO);

            Result<DayTradeProfitDO> result = actionService.doTradeActionByMonitor(actionInfo);
            if(result.getCode() == ResultUtil.CODE_SUCCESS) {
                DayTradeProfitDO data = result.getData();
                if(data != null)
                    actionInfo.setMatchProfit(data.getProfit());

                stockMsgNotifyService.onTradeNotify(actionInfo);
            }else{
                stockMsgNotifyService.onTradeNotifyFailMsg(actionInfo,result.getCode(), result.getMsg());
            }
        });
    }

    public static TradeActionInfo buildTradeActionInfo(RealtimeStockData realtimeStockData, GridTradePlanVO planDO,
                                                       String planType, TradeType tradeType){
        TradeActionInfo actionInfo = new TradeActionInfo();
        actionInfo.setPersonId(planDO.getCreateId());
        actionInfo.setPlanId(planDO.getId());
        actionInfo.setPlanType(planType);
        actionInfo.setComposeId(planDO.getComposeId());
        actionInfo.setCode(planDO.getStockCode());
        actionInfo.setRealtimeStockData(realtimeStockData);
        actionInfo.setTradeNumber(planDO.getTradeStockNum());
        actionInfo.setTradeType(tradeType);

        actionInfo.setPlanIncreasePer(planDO.getIncreasePer() == null ? "" : planDO.getIncreasePer().toString());
        actionInfo.setPlanDecreasePer(planDO.getDecreasePer() == null ? "" : planDO.getDecreasePer().toString());

        return actionInfo;
    }

    private void saveTradePlanPriceToRedis(String tradeKey, BigDecimal basePrice,
                                           BigDecimal increasePer, BigDecimal decreasePer,
                                           Long planId,
                                           GridTradePlanVarDO tradePlanVarDO){
        List<BigDecimal> priceList = PriceUtils.calcNextTradePriceList(basePrice, increasePer, decreasePer);

        putHashValue(tradeKey, StockConstants.KEY_GRID_NEXT_BUY_PRICE, priceList.get(0).toString());
        putHashValue(tradeKey, StockConstants.KEY_GRID_NEXT_SELL_PRICE, priceList.get(1).toString());

        if(tradePlanVarDO != null){
            tradePlanVarDO.setNextBuyPrice(priceList.get(0));
            tradePlanVarDO.setNextSellPrice(priceList.get(1));
            tradePlanVarDO.setPlanId(planId);
        }
    }

    private Object getHashValue(String oKey,String hKey){
        return redisTool.hGet(oKey, hKey);
    }

    private void putHashValue(String oKey,String hKey,String value){
        redisTool.hPut(oKey, hKey, value);
        expireTime(oKey, StockConstants.KEY_DEFAULT_EXPIRE_HOURS, TimeUnit.HOURS);
    }

    private void expireTime(String key, long timeout, TimeUnit timeUnit){
        redisTool.expire(key, timeout, timeUnit);
    }
}
